// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/macros.h"
#include "base/message_loop/message_loop.h"
#include "content/child/service_worker/service_worker_dispatcher.h"
#include "content/child/service_worker/service_worker_handle_reference.h"
#include "content/child/service_worker/service_worker_provider_context.h"
#include "content/child/service_worker/web_service_worker_impl.h"
#include "content/child/service_worker/web_service_worker_registration_impl.h"
#include "content/child/thread_safe_sender.h"
#include "content/common/service_worker/service_worker_messages.h"
#include "content/common/service_worker/service_worker_types.h"
#include "ipc/ipc_sync_message_filter.h"
#include "ipc/ipc_test_sink.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/WebKit/public/platform/modules/serviceworker/WebServiceWorkerProviderClient.h"

namespace content {

namespace {

    class ServiceWorkerTestSender : public ThreadSafeSender {
    public:
        explicit ServiceWorkerTestSender(IPC::TestSink* ipc_sink)
            : ThreadSafeSender(nullptr, nullptr)
            , ipc_sink_(ipc_sink)
        {
        }

        bool Send(IPC::Message* message) override
        {
            return ipc_sink_->Send(message);
        }

    private:
        ~ServiceWorkerTestSender() override { }

        IPC::TestSink* ipc_sink_;

        DISALLOW_COPY_AND_ASSIGN(ServiceWorkerTestSender);
    };

} // namespace

class ServiceWorkerDispatcherTest : public testing::Test {
public:
    ServiceWorkerDispatcherTest() { }

    void SetUp() override
    {
        sender_ = new ServiceWorkerTestSender(&ipc_sink_);
        dispatcher_.reset(new ServiceWorkerDispatcher(sender_.get(), nullptr));
    }

    void CreateObjectInfoAndVersionAttributes(
        ServiceWorkerRegistrationObjectInfo* info,
        ServiceWorkerVersionAttributes* attrs)
    {
        info->handle_id = 10;
        info->registration_id = 20;

        attrs->active.handle_id = 100;
        attrs->active.version_id = 200;
        attrs->waiting.handle_id = 101;
        attrs->waiting.version_id = 201;
        attrs->installing.handle_id = 102;
        attrs->installing.version_id = 202;
    }

    bool ContainsServiceWorker(int handle_id)
    {
        return ContainsKey(dispatcher_->service_workers_, handle_id);
    }

    bool ContainsRegistration(int registration_handle_id)
    {
        return ContainsKey(dispatcher_->registrations_, registration_handle_id);
    }

    void OnAssociateRegistration(int thread_id,
        int provider_id,
        const ServiceWorkerRegistrationObjectInfo& info,
        const ServiceWorkerVersionAttributes& attrs)
    {
        dispatcher_->OnAssociateRegistration(thread_id, provider_id, info, attrs);
    }

    void OnDisassociateRegistration(int thread_id, int provider_id)
    {
        dispatcher_->OnDisassociateRegistration(thread_id, provider_id);
    }

    void OnSetControllerServiceWorker(int thread_id,
        int provider_id,
        const ServiceWorkerObjectInfo& info,
        bool should_notify_controllerchange)
    {
        dispatcher_->OnSetControllerServiceWorker(thread_id, provider_id, info,
            should_notify_controllerchange);
    }

    void OnPostMessage(const ServiceWorkerMsg_MessageToDocument_Params& params)
    {
        dispatcher_->OnPostMessage(params);
    }

    std::unique_ptr<ServiceWorkerHandleReference> Adopt(
        const ServiceWorkerObjectInfo& info)
    {
        return dispatcher_->Adopt(info);
    }

    ServiceWorkerDispatcher* dispatcher() { return dispatcher_.get(); }
    ThreadSafeSender* thread_safe_sender() { return sender_.get(); }
    IPC::TestSink* ipc_sink() { return &ipc_sink_; }

private:
    base::MessageLoop message_loop_;
    IPC::TestSink ipc_sink_;
    std::unique_ptr<ServiceWorkerDispatcher> dispatcher_;
    scoped_refptr<ServiceWorkerTestSender> sender_;

    DISALLOW_COPY_AND_ASSIGN(ServiceWorkerDispatcherTest);
};

class MockWebServiceWorkerProviderClientImpl
    : public blink::WebServiceWorkerProviderClient {
public:
    MockWebServiceWorkerProviderClientImpl(int provider_id,
        ServiceWorkerDispatcher* dispatcher)
        : provider_id_(provider_id)
        , dispatcher_(dispatcher)
    {
        dispatcher_->AddProviderClient(provider_id, this);
    }

    ~MockWebServiceWorkerProviderClientImpl() override
    {
        dispatcher_->RemoveProviderClient(provider_id_);
    }

    void setController(std::unique_ptr<blink::WebServiceWorker::Handle> handle,
        bool shouldNotifyControllerChange) override
    {
        // WebPassOwnPtr cannot be owned in Chromium, so drop the handle here.
        // The destruction releases ServiceWorkerHandleReference.
        is_set_controlled_called_ = true;
    }

    void dispatchMessageEvent(
        std::unique_ptr<blink::WebServiceWorker::Handle> handle,
        const blink::WebString& message,
        const blink::WebMessagePortChannelArray& channels) override
    {
        // WebPassOwnPtr cannot be owned in Chromium, so drop the handle here.
        // The destruction releases ServiceWorkerHandleReference.
        is_dispatch_message_event_called_ = true;
    }

    bool is_set_controlled_called() const { return is_set_controlled_called_; }

    bool is_dispatch_message_event_called() const
    {
        return is_dispatch_message_event_called_;
    }

private:
    const int provider_id_;
    bool is_set_controlled_called_ = false;
    bool is_dispatch_message_event_called_ = false;
    ServiceWorkerDispatcher* dispatcher_;
};

TEST_F(ServiceWorkerDispatcherTest, OnAssociateRegistration_NoProviderContext)
{
    // Assume that these objects are passed from the browser process and own
    // references to browser-side registration/worker representations.
    ServiceWorkerRegistrationObjectInfo info;
    ServiceWorkerVersionAttributes attrs;
    CreateObjectInfoAndVersionAttributes(&info, &attrs);

    // The passed references should be adopted but immediately released because
    // there is no provider context to own the references.
    const int kProviderId = 10;
    OnAssociateRegistration(kDocumentMainThreadId, kProviderId, info, attrs);
    ASSERT_EQ(4UL, ipc_sink()->message_count());
    EXPECT_EQ(ServiceWorkerHostMsg_DecrementServiceWorkerRefCount::ID,
        ipc_sink()->GetMessageAt(0)->type());
    EXPECT_EQ(ServiceWorkerHostMsg_DecrementServiceWorkerRefCount::ID,
        ipc_sink()->GetMessageAt(1)->type());
    EXPECT_EQ(ServiceWorkerHostMsg_DecrementServiceWorkerRefCount::ID,
        ipc_sink()->GetMessageAt(2)->type());
    EXPECT_EQ(ServiceWorkerHostMsg_DecrementRegistrationRefCount::ID,
        ipc_sink()->GetMessageAt(3)->type());
}

TEST_F(ServiceWorkerDispatcherTest,
    OnAssociateRegistration_ProviderContextForController)
{
    // Assume that these objects are passed from the browser process and own
    // references to browser-side registration/worker representations.
    ServiceWorkerRegistrationObjectInfo info;
    ServiceWorkerVersionAttributes attrs;
    CreateObjectInfoAndVersionAttributes(&info, &attrs);

    // Set up ServiceWorkerProviderContext for ServiceWorkerGlobalScope.
    const int kProviderId = 10;
    scoped_refptr<ServiceWorkerProviderContext> provider_context(
        new ServiceWorkerProviderContext(kProviderId,
            SERVICE_WORKER_PROVIDER_FOR_CONTROLLER,
            thread_safe_sender()));

    // The passed references should be adopted and owned by the provider context.
    OnAssociateRegistration(kDocumentMainThreadId, kProviderId, info, attrs);
    EXPECT_EQ(0UL, ipc_sink()->message_count());

    // Destruction of the provider context should release references to the
    // associated registration and its versions.
    provider_context = nullptr;
    ASSERT_EQ(4UL, ipc_sink()->message_count());
    EXPECT_EQ(ServiceWorkerHostMsg_DecrementServiceWorkerRefCount::ID,
        ipc_sink()->GetMessageAt(0)->type());
    EXPECT_EQ(ServiceWorkerHostMsg_DecrementServiceWorkerRefCount::ID,
        ipc_sink()->GetMessageAt(1)->type());
    EXPECT_EQ(ServiceWorkerHostMsg_DecrementServiceWorkerRefCount::ID,
        ipc_sink()->GetMessageAt(2)->type());
    EXPECT_EQ(ServiceWorkerHostMsg_DecrementRegistrationRefCount::ID,
        ipc_sink()->GetMessageAt(3)->type());
}

TEST_F(ServiceWorkerDispatcherTest,
    OnAssociateRegistration_ProviderContextForControllee)
{
    // Assume that these objects are passed from the browser process and own
    // references to browser-side registration/worker representations.
    ServiceWorkerRegistrationObjectInfo info;
    ServiceWorkerVersionAttributes attrs;
    CreateObjectInfoAndVersionAttributes(&info, &attrs);

    // Set up ServiceWorkerProviderContext for a document context.
    const int kProviderId = 10;
    scoped_refptr<ServiceWorkerProviderContext> provider_context(
        new ServiceWorkerProviderContext(kProviderId,
            SERVICE_WORKER_PROVIDER_FOR_WINDOW,
            thread_safe_sender()));

    // The passed references should be adopted and only the registration reference
    // should be owned by the provider context.
    OnAssociateRegistration(kDocumentMainThreadId, kProviderId, info, attrs);
    ASSERT_EQ(3UL, ipc_sink()->message_count());
    EXPECT_EQ(ServiceWorkerHostMsg_DecrementServiceWorkerRefCount::ID,
        ipc_sink()->GetMessageAt(0)->type());
    EXPECT_EQ(ServiceWorkerHostMsg_DecrementServiceWorkerRefCount::ID,
        ipc_sink()->GetMessageAt(1)->type());
    EXPECT_EQ(ServiceWorkerHostMsg_DecrementServiceWorkerRefCount::ID,
        ipc_sink()->GetMessageAt(2)->type());
    ipc_sink()->ClearMessages();

    // Disassociating the provider context from the registration should release
    // the reference.
    OnDisassociateRegistration(kDocumentMainThreadId, kProviderId);
    ASSERT_EQ(1UL, ipc_sink()->message_count());
    EXPECT_EQ(ServiceWorkerHostMsg_DecrementRegistrationRefCount::ID,
        ipc_sink()->GetMessageAt(0)->type());
}

TEST_F(ServiceWorkerDispatcherTest, OnSetControllerServiceWorker)
{
    const int kProviderId = 10;
    bool should_notify_controllerchange = true;

    // Assume that these objects are passed from the browser process and own
    // references to browser-side registration/worker representations.
    ServiceWorkerRegistrationObjectInfo info;
    ServiceWorkerVersionAttributes attrs;
    CreateObjectInfoAndVersionAttributes(&info, &attrs);

    // (1) In the case there are no SWProviderContext and WebSWProviderClient for
    // the provider, the passed reference to the active worker should be adopted
    // but immediately released because there is no provider context to own it.
    OnSetControllerServiceWorker(kDocumentMainThreadId, kProviderId, attrs.active,
        should_notify_controllerchange);
    ASSERT_EQ(1UL, ipc_sink()->message_count());
    EXPECT_EQ(ServiceWorkerHostMsg_DecrementServiceWorkerRefCount::ID,
        ipc_sink()->GetMessageAt(0)->type());
    ipc_sink()->ClearMessages();

    // (2) In the case there is no WebSWProviderClient but SWProviderContext for
    // the provider, the passed referecence should be adopted and owned by the
    // provider context.
    scoped_refptr<ServiceWorkerProviderContext> provider_context(
        new ServiceWorkerProviderContext(kProviderId,
            SERVICE_WORKER_PROVIDER_FOR_WINDOW,
            thread_safe_sender()));
    OnAssociateRegistration(kDocumentMainThreadId, kProviderId, info, attrs);
    ipc_sink()->ClearMessages();
    OnSetControllerServiceWorker(kDocumentMainThreadId, kProviderId, attrs.active,
        should_notify_controllerchange);
    EXPECT_EQ(0UL, ipc_sink()->message_count());

    // Destruction of the provider context should release references to the
    // associated registration and the controller.
    provider_context = nullptr;
    ASSERT_EQ(2UL, ipc_sink()->message_count());
    EXPECT_EQ(ServiceWorkerHostMsg_DecrementServiceWorkerRefCount::ID,
        ipc_sink()->GetMessageAt(0)->type());
    EXPECT_EQ(ServiceWorkerHostMsg_DecrementRegistrationRefCount::ID,
        ipc_sink()->GetMessageAt(1)->type());
    ipc_sink()->ClearMessages();

    // (3) In the case there is no SWProviderContext but WebSWProviderClient for
    // the provider, the new reference should be created and owned by the provider
    // client (but the reference is immediately released due to limitation of the
    // mock provider client. See the comment on setController() of the mock).
    // In addition, the passed reference should be adopted but immediately
    // released because there is no provider context to own it.
    std::unique_ptr<MockWebServiceWorkerProviderClientImpl> provider_client(
        new MockWebServiceWorkerProviderClientImpl(kProviderId, dispatcher()));
    ASSERT_FALSE(provider_client->is_set_controlled_called());
    OnSetControllerServiceWorker(kDocumentMainThreadId, kProviderId, attrs.active,
        should_notify_controllerchange);
    EXPECT_TRUE(provider_client->is_set_controlled_called());
    ASSERT_EQ(3UL, ipc_sink()->message_count());
    EXPECT_EQ(ServiceWorkerHostMsg_IncrementServiceWorkerRefCount::ID,
        ipc_sink()->GetMessageAt(0)->type());
    EXPECT_EQ(ServiceWorkerHostMsg_DecrementServiceWorkerRefCount::ID,
        ipc_sink()->GetMessageAt(1)->type());
    EXPECT_EQ(ServiceWorkerHostMsg_DecrementServiceWorkerRefCount::ID,
        ipc_sink()->GetMessageAt(2)->type());
    provider_client.reset();
    ipc_sink()->ClearMessages();

    // (4) In the case there are both SWProviderContext and SWProviderClient for
    // the provider, the passed referecence should be adopted and owned by the
    // provider context. In addition, the new reference should be created for the
    // provider client and immediately released due to limitation of the mock
    // implementation.
    provider_context = new ServiceWorkerProviderContext(
        kProviderId, SERVICE_WORKER_PROVIDER_FOR_WINDOW, thread_safe_sender());
    OnAssociateRegistration(kDocumentMainThreadId, kProviderId, info, attrs);
    provider_client.reset(
        new MockWebServiceWorkerProviderClientImpl(kProviderId, dispatcher()));
    ASSERT_FALSE(provider_client->is_set_controlled_called());
    ipc_sink()->ClearMessages();
    OnSetControllerServiceWorker(kDocumentMainThreadId, kProviderId, attrs.active,
        should_notify_controllerchange);
    EXPECT_TRUE(provider_client->is_set_controlled_called());
    ASSERT_EQ(2UL, ipc_sink()->message_count());
    EXPECT_EQ(ServiceWorkerHostMsg_IncrementServiceWorkerRefCount::ID,
        ipc_sink()->GetMessageAt(0)->type());
    EXPECT_EQ(ServiceWorkerHostMsg_DecrementServiceWorkerRefCount::ID,
        ipc_sink()->GetMessageAt(1)->type());
}

// Test that clearing the controller by sending a kInvalidServiceWorkerHandle
// results in the provider context having a null controller.
TEST_F(ServiceWorkerDispatcherTest, OnSetControllerServiceWorker_Null)
{
    const int kProviderId = 10;
    bool should_notify_controllerchange = true;

    ServiceWorkerRegistrationObjectInfo info;
    ServiceWorkerVersionAttributes attrs;
    CreateObjectInfoAndVersionAttributes(&info, &attrs);

    std::unique_ptr<MockWebServiceWorkerProviderClientImpl> provider_client(
        new MockWebServiceWorkerProviderClientImpl(kProviderId, dispatcher()));
    scoped_refptr<ServiceWorkerProviderContext> provider_context(
        new ServiceWorkerProviderContext(kProviderId,
            SERVICE_WORKER_PROVIDER_FOR_WINDOW,
            thread_safe_sender()));

    OnAssociateRegistration(kDocumentMainThreadId, kProviderId, info, attrs);

    // Set the controller to kInvalidServiceWorkerHandle.
    OnSetControllerServiceWorker(kDocumentMainThreadId, kProviderId,
        ServiceWorkerObjectInfo(),
        should_notify_controllerchange);

    // Check that it became null.
    EXPECT_EQ(nullptr, provider_context->controller());
    EXPECT_TRUE(provider_client->is_set_controlled_called());
}

TEST_F(ServiceWorkerDispatcherTest, OnPostMessage)
{
    const int kProviderId = 10;

    // Assume that these objects are passed from the browser process and own
    // references to browser-side registration/worker representations.
    ServiceWorkerRegistrationObjectInfo info;
    ServiceWorkerVersionAttributes attrs;
    CreateObjectInfoAndVersionAttributes(&info, &attrs);

    ServiceWorkerMsg_MessageToDocument_Params params;
    params.thread_id = kDocumentMainThreadId;
    params.provider_id = kProviderId;
    params.service_worker_info = attrs.active;

    // The passed reference should be adopted but immediately released because
    // there is no provider client.
    OnPostMessage(params);
    ASSERT_EQ(1UL, ipc_sink()->message_count());
    EXPECT_EQ(ServiceWorkerHostMsg_DecrementServiceWorkerRefCount::ID,
        ipc_sink()->GetMessageAt(0)->type());
    ipc_sink()->ClearMessages();

    std::unique_ptr<MockWebServiceWorkerProviderClientImpl> provider_client(
        new MockWebServiceWorkerProviderClientImpl(kProviderId, dispatcher()));
    ASSERT_FALSE(provider_client->is_dispatch_message_event_called());

    // The passed reference should be owned by the provider client (but the
    // reference is immediately released due to limitation of the mock provider
    // client. See the comment on dispatchMessageEvent() of the mock).
    OnPostMessage(params);
    EXPECT_TRUE(provider_client->is_dispatch_message_event_called());
    ASSERT_EQ(1UL, ipc_sink()->message_count());
    EXPECT_EQ(ServiceWorkerHostMsg_DecrementServiceWorkerRefCount::ID,
        ipc_sink()->GetMessageAt(0)->type());
}

TEST_F(ServiceWorkerDispatcherTest, GetServiceWorker)
{
    ServiceWorkerRegistrationObjectInfo info;
    ServiceWorkerVersionAttributes attrs;
    CreateObjectInfoAndVersionAttributes(&info, &attrs);

    // Should return a worker object newly created with the given reference.
    scoped_refptr<WebServiceWorkerImpl> worker(
        dispatcher()->GetOrCreateServiceWorker(Adopt(attrs.installing)));
    EXPECT_TRUE(worker);
    EXPECT_TRUE(ContainsServiceWorker(attrs.installing.handle_id));
    EXPECT_EQ(0UL, ipc_sink()->message_count());

    // Should return the same worker object and release the given reference.
    scoped_refptr<WebServiceWorkerImpl> existing_worker = dispatcher()->GetOrCreateServiceWorker(Adopt(attrs.installing));
    EXPECT_EQ(worker, existing_worker);
    ASSERT_EQ(1UL, ipc_sink()->message_count());
    EXPECT_EQ(ServiceWorkerHostMsg_DecrementServiceWorkerRefCount::ID,
        ipc_sink()->GetMessageAt(0)->type());
    ipc_sink()->ClearMessages();

    // Should return nullptr when a given object is invalid.
    scoped_refptr<WebServiceWorkerImpl> invalid_worker = dispatcher()->GetOrCreateServiceWorker(Adopt(ServiceWorkerObjectInfo()));
    EXPECT_FALSE(invalid_worker);
    EXPECT_EQ(0UL, ipc_sink()->message_count());
}

TEST_F(ServiceWorkerDispatcherTest, GetOrCreateRegistration)
{
    ServiceWorkerRegistrationObjectInfo info;
    ServiceWorkerVersionAttributes attrs;
    CreateObjectInfoAndVersionAttributes(&info, &attrs);

    // Should return a registration object newly created with incrementing
    // the refcounts.
    scoped_refptr<WebServiceWorkerRegistrationImpl> registration1(
        dispatcher()->GetOrCreateRegistration(info, attrs));
    EXPECT_TRUE(registration1);
    EXPECT_TRUE(ContainsRegistration(info.handle_id));
    EXPECT_EQ(info.registration_id, registration1->registrationId());
    ASSERT_EQ(4UL, ipc_sink()->message_count());
    EXPECT_EQ(ServiceWorkerHostMsg_IncrementRegistrationRefCount::ID,
        ipc_sink()->GetMessageAt(0)->type());
    EXPECT_EQ(ServiceWorkerHostMsg_IncrementServiceWorkerRefCount::ID,
        ipc_sink()->GetMessageAt(1)->type());
    EXPECT_EQ(ServiceWorkerHostMsg_IncrementServiceWorkerRefCount::ID,
        ipc_sink()->GetMessageAt(2)->type());
    EXPECT_EQ(ServiceWorkerHostMsg_IncrementServiceWorkerRefCount::ID,
        ipc_sink()->GetMessageAt(3)->type());

    ipc_sink()->ClearMessages();

    // Should return the same registration object without incrementing the
    // refcounts.
    scoped_refptr<WebServiceWorkerRegistrationImpl> registration2(
        dispatcher()->GetOrCreateRegistration(info, attrs));
    EXPECT_TRUE(registration2);
    EXPECT_EQ(registration1, registration2);
    EXPECT_EQ(0UL, ipc_sink()->message_count());

    ipc_sink()->ClearMessages();

    // The registration dtor decrements the refcounts.
    registration1 = nullptr;
    registration2 = nullptr;
    ASSERT_EQ(4UL, ipc_sink()->message_count());
    EXPECT_EQ(ServiceWorkerHostMsg_DecrementServiceWorkerRefCount::ID,
        ipc_sink()->GetMessageAt(0)->type());
    EXPECT_EQ(ServiceWorkerHostMsg_DecrementServiceWorkerRefCount::ID,
        ipc_sink()->GetMessageAt(1)->type());
    EXPECT_EQ(ServiceWorkerHostMsg_DecrementServiceWorkerRefCount::ID,
        ipc_sink()->GetMessageAt(2)->type());
    EXPECT_EQ(ServiceWorkerHostMsg_DecrementRegistrationRefCount::ID,
        ipc_sink()->GetMessageAt(3)->type());
}

TEST_F(ServiceWorkerDispatcherTest, GetOrAdoptRegistration)
{
    ServiceWorkerRegistrationObjectInfo info;
    ServiceWorkerVersionAttributes attrs;
    CreateObjectInfoAndVersionAttributes(&info, &attrs);

    // Should return a registration object newly created with adopting the
    // refcounts.
    scoped_refptr<WebServiceWorkerRegistrationImpl> registration1(
        dispatcher()->GetOrAdoptRegistration(info, attrs));
    EXPECT_TRUE(registration1);
    EXPECT_TRUE(ContainsRegistration(info.handle_id));
    EXPECT_EQ(info.registration_id, registration1->registrationId());
    EXPECT_EQ(0UL, ipc_sink()->message_count());

    ipc_sink()->ClearMessages();

    // Should return the same registration object without incrementing the
    // refcounts.
    scoped_refptr<WebServiceWorkerRegistrationImpl> registration2(
        dispatcher()->GetOrAdoptRegistration(info, attrs));
    EXPECT_TRUE(registration2);
    EXPECT_EQ(registration1, registration2);
    ASSERT_EQ(4UL, ipc_sink()->message_count());
    EXPECT_EQ(ServiceWorkerHostMsg_DecrementServiceWorkerRefCount::ID,
        ipc_sink()->GetMessageAt(0)->type());
    EXPECT_EQ(ServiceWorkerHostMsg_DecrementServiceWorkerRefCount::ID,
        ipc_sink()->GetMessageAt(1)->type());
    EXPECT_EQ(ServiceWorkerHostMsg_DecrementServiceWorkerRefCount::ID,
        ipc_sink()->GetMessageAt(2)->type());
    EXPECT_EQ(ServiceWorkerHostMsg_DecrementRegistrationRefCount::ID,
        ipc_sink()->GetMessageAt(3)->type());

    ipc_sink()->ClearMessages();

    // The registration dtor decrements the refcounts.
    registration1 = nullptr;
    registration2 = nullptr;
    ASSERT_EQ(4UL, ipc_sink()->message_count());
    EXPECT_EQ(ServiceWorkerHostMsg_DecrementServiceWorkerRefCount::ID,
        ipc_sink()->GetMessageAt(0)->type());
    EXPECT_EQ(ServiceWorkerHostMsg_DecrementServiceWorkerRefCount::ID,
        ipc_sink()->GetMessageAt(1)->type());
    EXPECT_EQ(ServiceWorkerHostMsg_DecrementServiceWorkerRefCount::ID,
        ipc_sink()->GetMessageAt(2)->type());
    EXPECT_EQ(ServiceWorkerHostMsg_DecrementRegistrationRefCount::ID,
        ipc_sink()->GetMessageAt(3)->type());
}

} // namespace content
